﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Lib.Web.Mvc.JQuery.JqGrid;
using jqGrid.Models.NorthWind;
using jqGrid.Repositories.NorthWind;
using System.Text;
using jqGrid.Models;
using System.IO;
using jqGrid.Repositories;

namespace jqGrid.Controllers
{
    public class HomeController : Controller
    {
        #region Fields
        IProductsRepository _productsRepository;
        ISuppliersRepository _suppliersRepository;
        ICategoriesRepository _categoriesRepository;
        IOrdersRepository _ordersRepository;
        #endregion

        #region Constructors
        public HomeController()
            : this(new ProductsRepository(), new SuppliersRepository(), new CategoriesRepository(), new OrdersRepository())
        {
        }

        public HomeController(IProductsRepository productsRepository, ISuppliersRepository suppliersRepository, ICategoriesRepository categoriesRepository, IOrdersRepository ordersRepository)
        {
            _productsRepository = productsRepository;
            _suppliersRepository = suppliersRepository;
            _categoriesRepository = categoriesRepository;
            _ordersRepository = ordersRepository;
        }
        #endregion

        #region Actions
        public ActionResult Basics()
        {
            return View();
        }

        public ActionResult Formatting()
        {
            return View();
        }

        public ActionResult DynamicScrolling()
        {
            return View();
        }

        public ActionResult Grouping()
        {
            return View();
        }

        public ActionResult Configuration()
        {
            return View();
        }

        public ActionResult Loadonce()
        {
            return View();
        }

        public ActionResult JQueryUIIntegration()
        {
            return View();
        }

        /// <summary>
        /// Import configuration for jqGrid in json
        /// </summary>
        /// <param name="id">The jqGrid identifier</param>
        /// <returns>The jqGrid configuration in json format</returns>
        [Lib.Web.Mvc.NoCache]
        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult ImportConfiguration(string id)
        {
            JqGridConfiguration configuration = null;
            //Do we already have configuration in Session
            if (Session[id] != null)
                //If yes, the use it
                configuration = (JqGridConfiguration)Session[id];
            else
            {
                //If no, then create one
                configuration = new JqGridConfiguration()
                {
                    Settings = new JqGridOptions(id)
                    {
                        ColumnsRemaping = new List<int>() { 0, 1, 2, 3, 4, 5, 6 },
                        RowsNumber = 10,
                        SortingName = "ProductID",
                        SortingOrder = JqGridSortingOrders.Asc,
                        ViewRecords = true
                    }
                };
                configuration.Settings.ColumnsNames.AddRange(new string[] { "ProductID", "ProductName", "Supplier", "Category", "QuantityPerUnit", "UnitPrice", "UnitsInStock" });
                configuration.Settings.ColumnsModels.AddRange(new JqGridColumnModel[]
                {
                    new JqGridColumnModel("ProductID") { Index = "ProductID" },
                    new JqGridColumnModel("ProductName") {  Index = "ProductName" },
                    new JqGridColumnModel("Supplier") {  Index = "SupplierID" },
                    new JqGridColumnModel("Category") {  Index = "Category" },
                    new JqGridColumnModel("QuantityPerUnit") {  Index = "QuantityPerUnit" },
                    new JqGridColumnModel("UnitPrice") {  Index = "UnitPrice" },
                    new JqGridColumnModel("UnitsInStock") {  Index = "UnitsInStock" }
                });
            }

            //Those settings should be set always "just in case"
            configuration.Settings.Url = Url.Action("Products", "Home");
            configuration.Settings.DataType = JqGridDataTypes.Json;
            configuration.Settings.MethodType = JqGridMethodTypes.Post;
            configuration.Settings.Pager = true;
            configuration.Settings.JsonReader.RepeatItems = false;
            configuration.Settings.JsonReader.RecordId = "ProductID";

            return new JqGridJsonResult() { Data = configuration };
        }

        /// <summary>
        /// Exports configuration for jqGrid
        /// </summary>
        /// <param name="configuration">The jqGrid configuration</param>
        /// <returns>An empty result</returns>
        [AcceptVerbs(HttpVerbs.Post)]
        public EmptyResult ExportConfiguration(JqGridConfiguration configuration)
        {
            //Store configuration in Session 
            Session[configuration.Settings.Id] = configuration;
            //Return empty result, it will be ignored
            return new EmptyResult();
        }

        public ActionResult SingleSearching()
        {
            return View();
        }

        public ActionResult ToolbarSearching()
        {
            return View();
        }

        public ActionResult CustomSearching()
        {
            return View();
        }

        public ActionResult AdvancedSearching()
        {
            return View();
        }

        public ActionResult AdvancedSearchingTemplates()
        {
            return View();
        }

        public ActionResult CellEditing()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult UpdateProductProperty(CellEditingViewModel viewModel)
        {
            Product product = _productsRepository.GetProduct(viewModel.ProductID);
            switch (viewModel.PropertyName)
            {
                case "ProductName":
                    product.ProductName = (string)viewModel.PropertyValue;
                    break;
                case "CategoryID":
                    product.CategoryID = (int)viewModel.PropertyValue;
                    break;
                case "QuantityPerUnit":
                    product.QuantityPerUnit = (string)viewModel.PropertyValue;
                    break;
                case "UnitPrice":
                    product.UnitPrice = (decimal)viewModel.PropertyValue;
                    break;
            }

            try
            {
                _productsRepository.SubmitChanges();
            }
            catch
            {
                return Json(false);
            }

            return Json(true);
        }

        public ActionResult InlineEditing()
        {
            return View();
        }

        public ActionResult FormEditing()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Products(JqGridRequest request, CustomSearchViewModel viewModel)
        {
            string filterExpression = String.Empty;
            if (request.Searching)
            {
                //Single searching
                if (request.SearchingFilter != null)
                    filterExpression = GetFilter(request.SearchingFilter.SearchingName, request.SearchingFilter.SearchingOperator, request.SearchingFilter.SearchingValue);
                //Advanced searching, Toolbar searching (stringResult = true)
                else if (request.SearchingFilters != null)
                {
                    StringBuilder filterExpressionBuilder = new StringBuilder();
                    string groupingOperatorToString = request.SearchingFilters.GroupingOperator.ToString();
                    foreach (JqGridRequestSearchingFilter searchingFilter in request.SearchingFilters.Filters)
                    {
                        filterExpressionBuilder.Append(GetFilter(searchingFilter.SearchingName, searchingFilter.SearchingOperator, searchingFilter.SearchingValue));
                        filterExpressionBuilder.Append(String.Format(" {0} ", groupingOperatorToString));
                    }
                    if (filterExpressionBuilder.Length > 0)
                        filterExpressionBuilder.Remove(filterExpressionBuilder.Length - groupingOperatorToString.Length - 2, groupingOperatorToString.Length + 2);
                    filterExpression = filterExpressionBuilder.ToString();
                }
                //Custom search
                else if (viewModel != null)
                    filterExpression = viewModel.GetFilterExpression();
            }

            int totalRecords = _productsRepository.GetProductsCount(filterExpression);

            //Fix for grouping, because it adds column name instead of index to SortingName
            string sortingName = request.SortingName.Replace("Category", "CategoryID");

            //Prepare JqGridData instance
            JqGridResponse response = new JqGridResponse()
            {
                //Total pages count
                TotalPagesCount = (int)Math.Ceiling((float)totalRecords / (float)request.RecordsCount),
                //Page number
                PageIndex = request.PageIndex,
                //Total records count
                TotalRecordsCount = totalRecords,
                //Footer data
                UserData = new { QuantityPerUnit = "Avg Unit Price:", UnitPrice = _productsRepository.GetProducts(filterExpression).Average(p => p.UnitPrice) }
            };
            //Table with rows data
            foreach (Product product in _productsRepository.GetProducts(filterExpression.ToString(), sortingName, request.SortingOrder.ToString(), request.PageIndex, request.RecordsCount, request.PagesCount.HasValue ? request.PagesCount.Value : 1))
            {
                response.Records.Add(new JqGridRecord(Convert.ToString(product.ProductID), new
                {
                    ProductID = product.ProductID,
                    ProductName = product.ProductName,
                    Supplier = String.Format("[{0}] {1}", product.Supplier.SupplierID, product.Supplier.CompanyName),
                    Category = product.Category.CategoryName,
                    QuantityPerUnit = product.QuantityPerUnit,
                    UnitPrice = product.UnitPrice,
                    UnitsInStock = product.UnitsInStock
                }));
            }

            //Return data as json
            response.Reader.RepeatItems = false;
            return new JqGridJsonResult() { Data = response };
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult InsertProduct(ProductViewModel viewModel)
        {
            Product product = new Product();
            product.ProductName = viewModel.ProductName;
            product.SupplierID = viewModel.SupplierID;
            product.CategoryID = viewModel.CategoryID;
            product.QuantityPerUnit = viewModel.QuantityPerUnit;
            product.UnitPrice = viewModel.UnitPrice;
            product.UnitsInStock = viewModel.UnitsInStock;
            product.Discontinued = false;

            return Json(_productsRepository.AddProduct(product));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult UpdateProduct(ProductViewModel viewModel)
        {
            Product product = _productsRepository.GetProduct(viewModel.ProductID.Value);
            product.ProductName = viewModel.ProductName;
            product.SupplierID = viewModel.SupplierID;
            product.CategoryID = viewModel.CategoryID;
            product.QuantityPerUnit = viewModel.QuantityPerUnit;
            product.UnitPrice = viewModel.UnitPrice;
            product.UnitsInStock = viewModel.UnitsInStock;

            try
            {
                _productsRepository.SubmitChanges();
            }
            catch
            {
                return Json(false);
            }

            return Json(true);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult UpdateUnitsInStock(int id, short unitsInStock)
        {
            Product product = _productsRepository.GetProduct(id);
            product.UnitsInStock = unitsInStock;

            try
            {
                _productsRepository.SubmitChanges();
            }
            catch
            {
                return Json(false);
            }

            return Json(true);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult DeleteProduct(int id)
        {
            return Json(_productsRepository.DeleteProduct(id));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Supplier(int supplierId)
        {
            Supplier supplier = _suppliersRepository.GetSupplier(supplierId);

            var supplierData = new
            {
                CompanyName = supplier.CompanyName,
                Address = supplier.Address,
                PostalCode = supplier.PostalCode,
                City = supplier.City,
                Country = supplier.Country,
                Phone = supplier.Phone,
                HomePage = supplier.HomePage
            };

            return Json(supplierData);
        }

        public ActionResult Suppliers()
        {
            Dictionary<int, string> suppliers = new Dictionary<int, string>();
            foreach (Supplier supplier in _suppliersRepository.GetSuppliers())
                suppliers.Add(supplier.SupplierID, supplier.CompanyName);
            return PartialView("Select", suppliers);
        }

        public ActionResult Categories()
        {
            Dictionary<int, string> categories = new Dictionary<int, string>();
            foreach (Category category in _categoriesRepository.GetCategories())
                categories.Add(category.CategoryID, category.CategoryName);
            return PartialView("Select", categories);
        }

        public ActionResult ProductsNames(string term)
        {
            return Json(_productsRepository.GetProducts(String.Format("ProductName.StartsWith(\"{0}\")", term)).Select(p => p.ProductName).ToArray(), JsonRequestBehavior.AllowGet);
        }

        public ActionResult Subgrid()
        {
            return View();
        }

        public ActionResult SubgridAsGrid()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Orders(JqGridRequest request)
        {
            int totalRecords = _ordersRepository.GetOrdersCount();

            JqGridResponse response = new JqGridResponse()
            {
                TotalPagesCount = (int)Math.Ceiling((float)totalRecords / (float)request.RecordsCount),
                PageIndex = request.PageIndex,
                TotalRecordsCount = totalRecords
            };
            foreach (Order order in _ordersRepository.GetOrders(request.SortingName, request.SortingOrder.ToString(), request.PageIndex, request.RecordsCount))
            {
                response.Records.Add(new JqGridRecord(Convert.ToString(order.OrderID), new List<object>()
                {
                    order.OrderID,
                    order.Customer.CompanyName,
                    order.Employee.FirstName + " " + order.Employee.LastName,
                    order.OrderDate.ToString(),
                    order.RequiredDate.ToString(),
                    order.ShippedDate.ToString(),
                    order.ShipCountry
                }));
            }

            return new JqGridJsonResult() { Data = response };
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult OrderDetails(int id)
        {
            JqGridResponse response = new JqGridResponse();
            foreach (OrderDetail orderDetails in _ordersRepository.GetOrderDetails(id))
            {
                response.Records.Add(new JqGridRecord(null, new List<object>()
                {
                    orderDetails.Product.ProductName,
                    orderDetails.UnitPrice.ToString(),
                    orderDetails.Quantity.ToString(),
                    orderDetails.Discount.ToString()
                }));
            }

            return new JqGridJsonResult() { Data = response };
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult OrderDetailsSubgridAsGrid(int id, JqGridRequest request)
        {
            IEnumerable<OrderDetail> details = _ordersRepository.GetOrderDetails(id);
            int totalRecords = details.Count();

            JqGridResponse response = new JqGridResponse()
            {
                TotalPagesCount = (int)Math.Ceiling((float)totalRecords / (float)request.RecordsCount),
                PageIndex = request.PageIndex,
                TotalRecordsCount = totalRecords
            };
            foreach (OrderDetail orderDetails in details.Skip(request.PageIndex * request.RecordsCount).Take(request.RecordsCount))
            {
                response.Records.Add(new JqGridRecord(Convert.ToString(orderDetails.ProductID), new List<object>()
                {
                    orderDetails.Product.ProductName,
                    orderDetails.UnitPrice.ToString(),
                    orderDetails.Quantity.ToString(),
                    orderDetails.Discount.ToString()
                }));
            }

            return new JqGridJsonResult() { Data = response };
        }

        public ActionResult TreeGrid()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult FileSystemInfos(int? nodeid)
        {

            DirectoryInfo root = FileSystemInfosRepository.GetDirectoryInfo(nodeid);
            IEnumerable<FileSystemInfo> children = FileSystemInfosRepository.GetFileSystemInfos(nodeid);

            if (children != null)
            {
                JqGridResponse response = new JqGridResponse();
                response.Records.AddRange(from child in children
                                          select new JqGridAdjacencyTreeRecord(
                                              Convert.ToString(FileSystemInfosRepository.GetTreeGridId(child)),
                                              new List<object>()
                                              {
                                                  child.Name,
                                                  child.CreationTime.ToString(),
                                                  child.LastAccessTime.ToString(),
                                                  child.LastWriteTime.ToString(),
                                              },
                                              ((from backslash in child.FullName where backslash == '\\' select backslash).Count() - 2),
                                              Convert.ToString(FileSystemInfosRepository.GetTreeGridId(root))) { Leaf = (child is FileInfo) });

                return new JqGridJsonResult() { Data = response };
            }
            else
                return new EmptyResult();
        }
        #endregion

        #region Methods
        private string GetFilter(string searchingName, JqGridSearchOperators searchingOperator, string searchingValue)
        {
            string searchingOperatorString = String.Empty;
            switch (searchingOperator)
            {
                case JqGridSearchOperators.Eq:
                    searchingOperatorString = "=";
                    break;
                case JqGridSearchOperators.Ne:
                    searchingOperatorString = "!=";
                    break;
                case JqGridSearchOperators.Lt:
                    searchingOperatorString = "<";
                    break;
                case JqGridSearchOperators.Le:
                    searchingOperatorString = "<=";
                    break;
                case JqGridSearchOperators.Gt:
                    searchingOperatorString = ">";
                    break;
                case JqGridSearchOperators.Ge:
                    searchingOperatorString = ">=";
                    break;
            }

            searchingName = searchingName.Replace("Category", "CategoryID");
            if ((searchingName == "ProductID") || (searchingName == "SupplierID") || (searchingName == "CategoryID"))
                return String.Format("{0} {1} {2}", searchingName, searchingOperatorString, searchingValue);

            if ((searchingName == "ProductName"))
            {
                switch (searchingOperator)
                {
                    case JqGridSearchOperators.Bw:
                        return String.Format("{0}.StartsWith(\"{1}\")", searchingName, searchingValue);
                    case JqGridSearchOperators.Bn:
                        return String.Format("!{0}.StartsWith(\"{1}\")", searchingName, searchingValue);
                    case JqGridSearchOperators.Ew:
                        return String.Format("{0}.EndsWith(\"{1}\")", searchingName, searchingValue);
                    case JqGridSearchOperators.En:
                        return String.Format("!{0}.EndsWith(\"{1}\")", searchingName, searchingValue);
                    case JqGridSearchOperators.Cn:
                        return String.Format("{0}.Contains(\"{1}\")", searchingName, searchingValue);
                    case JqGridSearchOperators.Nc:
                        return String.Format("!{0}.Contains(\"{1}\")", searchingName, searchingValue);
                    default:
                        return String.Format("{0} {1} \"{2}\"", searchingName, searchingOperatorString, searchingValue);
                }
            }

            return String.Empty;
        }
        #endregion
    }
}
